﻿using System;
using Moki.Core;

namespace Moki
{
    internal class Call : ICall, ISetCall
    {
        private StubContext _stubContext;
        private StubPerformer _stubPerformer; 

        internal Call(StubContext call)
        {
            _stubContext = call; 
        }

        internal Call(StubContext call, StubPerformer stubPerformer)
        {
            _stubContext = call;
            _stubPerformer = stubPerformer;
        }

        /// <summary>
        /// Invokes the specified callback action.
        /// </summary>
        /// <param name="callbackAction">The callback action.</param>
        /// <returns></returns>
        public Call Callback(Action callbackAction)
        {
            return Callback(args => callbackAction());
        }

        /// <summary>
        /// Invokes the specified callback action.
        /// </summary>
        /// <param name="callbackAction">The callback action.</param>
        /// <returns></returns>
        public Call Callback(Action<Arguments> callbackAction)
        {
            _stubContext.AddCallBackToResult(callbackAction);
            return new Call(_stubContext, _stubPerformer);
        }

        /// <summary>
        /// Returns the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public ICall Returns(object value)
        {
            _stubContext.AddResultValue(value);
            return new Call(_stubContext, _stubPerformer);
        }

        /// <summary>
        /// Returns the specified values.
        /// </summary>
        /// <param name="values">The values.</param>
        /// <returns></returns>
        public ICall Returns(params object[] values)
        {
            foreach (var value in values)
                _stubContext.AddResultValue(value);

            return new Call(_stubContext, _stubPerformer);
        }

        /// <summary>
        /// Throws the specified exception.
        /// </summary>
        /// <param name="exception">The exception.</param>
        /// <returns></returns>
        public ICall Throws(Exception exception)
        {
            _stubContext.AddResultThrowException((Func<object>)(() => { throw exception; }));
            return new Call(_stubContext, _stubPerformer);
        }

        /// <summary>
        /// Does the specified implementation.
        /// </summary>
        /// <param name="implementation">The implementation.</param>
        /// <returns></returns>
        public ICall Do(Func<Arguments, object> implementation)
        {
            _stubContext.AddResultImplementation(implementation);
            return new Call(_stubContext, _stubPerformer);
        }

        /// <summary>
        /// Does the specified implementation.
        /// </summary>
        /// <param name="implementation">The implementation.</param>
        /// <returns></returns>
        public ICall Do(Func<object> implementation)
        {
            _stubContext.AddResultImplementation(implementation);
            return new Call(_stubContext, _stubPerformer);
        }

        /// <summary>
        /// Raises the specified event action.
        /// </summary>
        /// <param name="eventAction">The event action.</param>
        /// <param name="eventArgs">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        /// <returns></returns>
        public Call Raise(Action eventAction, EventArgs eventArgs)
        {
            return Raise(eventAction, _stubContext.Method, eventArgs);
        }

        /// <summary>
        /// Raises the specified event action.
        /// </summary>
        /// <param name="eventAction">The event action.</param>
        /// <param name="eventArgs">The event args.</param>
        /// <returns></returns>
        public Call Raise(Action eventAction, params object[] eventArgs)
        {
            _stubPerformer.BeginStubbing();
            eventAction();
            _stubContext.AddEventArgs(eventArgs);
            _stubPerformer.EndStubbing();
            return new Call(_stubContext, _stubPerformer);
        }

        /// <summary>
        /// Invokes the specified callback action.
        /// </summary>
        /// <param name="callbackAction">The callback action.</param>
        /// <returns></returns>
        ISetCall ISetCall.Callback(Action callbackAction)
        {
            return Callback(callbackAction);
        }

        /// <summary>
        /// Invokes the specified callback action.
        /// </summary>
        /// <param name="callbackAction">The callback action.</param>
        /// <returns></returns>
        ISetCall ISetCall.Callback(Action<Arguments> callbackAction)
        {
            return Callback(callbackAction);
        }

        /// <summary>
        /// Raises the specified event action.
        /// </summary>
        /// <param name="eventAction">The event action.</param>
        /// <param name="eventArgs">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        /// <returns></returns>
        ISetCall ISetCall.Raises(Action eventAction, EventArgs eventArgs)
        {
            return Raise(eventAction, eventArgs);
        }

        /// <summary>
        /// Raises the specified event action.
        /// </summary>
        /// <param name="eventAction">The event action.</param>
        /// <param name="eventArgs">The event args.</param>
        /// <returns></returns>
        ISetCall ISetCall.Raises(Action eventAction, params object[] eventArgs)
        {
            return Raise(eventAction, eventArgs);
        }

        /// <summary>
        /// Invokes the specified callback action.
        /// </summary>
        /// <param name="callbackAction">The callback action.</param>
        /// <returns></returns>
        ICall ICall.Callback(Action callbackAction)
        {
            return Callback(callbackAction);
        }

        /// <summary>
        /// Invokes the specified callback action.
        /// </summary>
        /// <param name="callbackAction">The callback action.</param>
        /// <returns></returns>
        ICall ICall.Callback(Action<Arguments> callbackAction)
        {
            return Callback(callbackAction);
        }

        /// <summary>
        /// Raises the specified action.
        /// </summary>
        /// <param name="action">The action.</param>
        /// <param name="eventArgs">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        /// <returns></returns>
        ICall ICall.Raises(Action action, EventArgs eventArgs)
        {
            return Raise(action, eventArgs);
        }

        /// <summary>
        /// Raises the specified event action.
        /// </summary>
        /// <param name="eventAction">The event action.</param>
        /// <param name="eventArgs">The event args.</param>
        /// <returns></returns>
        ICall ICall.Raises(Action eventAction, params object[] eventArgs)
        {
            return Raise(eventAction, eventArgs);
        }
    }
}
